using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using LibNDSFormats;
using LibNDSFormats.NSBMD;
using Tao.OpenGl;
using System.Drawing;

namespace MKDS_Course_Editor.MDLF
{
    public class MDLF
    {
        public List<MDLFObject> Objects = new List<MDLFObject>();
        public TEXS_.TEXS Textures = new TEXS_.TEXS();
        private float SCALE_IV = 4096f;
        public static MDLF Read(string file)
        {
            MDLF f = new MDLF();
            try
            {
                f.Textures = TEXS_.Read(file);
                f.MakeTexture(f);
            }
            catch { }
            EndianBinaryReader er = new EndianBinaryReader(File.OpenRead(file), Endianness.LittleEndian);
            if (er.ReadString(Encoding.ASCII, 4) == "HBDF")
            {
                er.ReadInt32();
                if (er.ReadString(Encoding.ASCII, 4) == "MDLF")
                {
                    er.ReadBytes(0x2C);
                    int nr = er.ReadInt16();
                    er.ReadInt16();
                    bool found = false;
                    while (er.BaseStream.Length - er.BaseStream.Position != 4)
                    {
                        string s = er.ReadString(Encoding.ASCII, 4);
                        if (s == "OBJO") // "TEX0"
                        {
                            //startpos = (int)er.BaseStream.Position - 4;
                            er.BaseStream.Position-=4;
                            found = true;
                            break;
                        }
                    }
                    if (found)
                    {
                        try
                        {
                            for (int i = 0; i < nr; i++)
                            {
                                if (er.ReadString(Encoding.ASCII, 4) != "OBJO")
                                    break;
                                int l = er.ReadInt32();
                                if (l <= 0x30)
                                {
                                    er.ReadBytes(l);
                                    continue;
                                }
                                f.Objects.Add(new MDLFObject());
                                er.ReadBytes(0x30);
                                er.ReadInt32();
                                int tot = er.ReadInt32();
                                er.ReadBytes(0x1E);
                                int length = er.ReadInt16();
                                er.ReadBytes(tot-0x1E-2-length);
                                f.Objects[i].Mesh.primitives = er.ReadBytes(length);
                            }

                        }
                        catch { }
                    }
                    else
                    {
                        MessageBox.Show("Error");
                        er.Close();
                    }
                }
                else
                {
                    MessageBox.Show("Error");
                    er.Close();
                }
            }
            else
            {
                MessageBox.Show("Error");
                er.Close();
            }
            er.Close();
            return f;
        }
        public void Render()
        {
            for(int i = 0; i<Objects.Count;i++)
            {
                        int matid = i;//poly.MatId;

                        if (matid == -1)
                        {
                            Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
                        }
                        else
                        {
                            //var mat = Model.Materials[matid];
                            //if ((mat.format == 1 || mat.format == 6) && r != RenderMode.Translucent) continue;
                            //if ((mat.format == 0 || mat.format == 2 || mat.format == 3 || mat.format == 4 || mat.format == 5 || mat.format == 7) && r != RenderMode.Opaque) continue;
                            Gl.glBindTexture(Gl.GL_TEXTURE_2D, matid + 1);
                            // Convert pixel coords to normalised STs
                            Gl.glMatrixMode(Gl.GL_TEXTURE);
                            Gl.glLoadIdentity();
                            Gl.glScalef(1.0f / Textures.IMGO[i].Width, 1.0f / Textures.IMGO[i].Height, 1.0f);

                        }
                if(Objects[i].Mesh.primitives != null)
                {
                    if (Objects[i].Mesh.primitives.Length != 0)
                    {
                        if (Objects[i].Mesh.primitives[0] == 0x40)
                        {
                            RenderPrimitives(Objects[i].Mesh.primitives);
                        }
                    }
                }
            }
        }
        private static readonly String[] TEXTURE_FORMATS = new String[] { "", "A3I5", "4-Color", "16-Color", "256-Color", "4x4-Texel", "A5I3", "Direct Texture" };
        /// <summary>
        /// Make texture for model.
        /// </summary>
        /// <param name="mod">NSBMD Model</param>
        private void MakeTexture(MDLF mod)
        {
            Console.WriteLine("DEBUG: making texture for model '{0}'...", "");

            for (int i = 0; i < mod.Textures.NrTex; i++)
            {
                try
                {
                    if (mod.Textures.IMGO[i].TexFormat == 0) // format 0 is no texture
                        continue;
                    var mat = mod.Textures.IMGO[i];
                    if ((mod.Textures.PLTO[i].Pal == null && mat.TexFormat != 7))
                        continue;
                    int pixelnum = mat.Width * mat.Height;
                    Color[] pal = mod.Textures.PLTO[i].Pal;

                    var image = new RGBA[pixelnum];


                    switch (mat.TexFormat)
                    {
                        // No Texture
                        case 0:
                            //puts( "ERROR: format 0" );
                            continue;
                            break;
                        // A3I5 Translucent Texture (3bit Alpha, 5bit Color Index)
                        case 1:
                            for (int j = 0; j < pixelnum; j++)
                            {
                                int index = mat.Image[j] & 0x1f;
                                int alpha = (mat.Image[j] >> 5);// & 7;
                                alpha = ((alpha * 4) + (alpha / 2)) * 8;// << 3;
                                image[j] = RGBA.fromColor(pal[index]);
                                image[j].A = (byte)alpha;
                            }

                            break;
                        // 4-Color Palette Texture
                        case 2:
                            for (int j = 0; j < pixelnum; j++)
                            {
                                uint index = mat.Image[j / 4];
                                index = (index >> ((j % 4) << 1)) & 3;
                                image[j] = RGBA.fromColor(pal[index]);
                            }
                            break;
                        // 16-Color Palette Texture
                        case 3:
                            //if (mat.color0 != 0) mat.paldata[0] = RGBA.Transparent; // made palette entry 0 transparent
                            for (int j = 0; j < pixelnum; j++)
                            {
                                var matindex = j / 2;
                                if (mat.Image.Length < matindex)
                                    continue;
                                int index = mat.Image[matindex];
                                index = (index >> ((j % 2) << 2)) & 0x0f;
                                if (pal == null)
                                    continue;
                                if (index < 0 || index >= pal.Length)
                                    continue;
                                if (j < 0 || j >= pixelnum)
                                    continue;
                                image[j] = RGBA.fromColor(pal[index]);
                            }
                            break;
                        // 256-Color Palette Texture
                        case 4:
                            //if (mat.color0 != 0) pal[0] = RGBA.Transparent; // made palette entry 0 transparent
                            // made palette entry 0 transparent
                            for (int j = 0; j < pixelnum; j++)
                            {
                                image[j] = RGBA.fromColor(pal[mat.Image[j]]);
                            }
                            break;
                        // 4x4-Texel Compressed Texture
                        case 5:
                            convert_4x4texel_b(mat.Image, mat.Width, mat.Height, mat.spData, pal, image);
                            break;
                        // A5I3 Translucent Texture (5bit Alpha, 3bit Color Index)
                        case 6:
                            for (int j = 0; j < pixelnum; j++)
                            {
                                int index = mat.Image[j] & 0x7;
                                int alpha = (mat.Image[j] >> 3);
                                alpha *= 8; //((alpha * 4) + (alpha / 2)) << 3;
                                image[j] = RGBA.fromColor(pal[index]);
                                image[j].A = (byte)alpha;
                            }
                            break;
                        // Direct Color Texture
                        case 7:
                            for (int j = 0; j < pixelnum; j++)
                            {
                                //UInt16 p = (ushort)(mat.texdata[j * 2] + (mat.texdata[j * 2 + 1] << 8));
                                //image[j].R = (byte)(((p >> 0) & 0x1f) << 3);
                                //image[j].G = (byte)(((p >> 5) & 0x1f) << 3);
                                //image[j].B = (byte)(((p >> 10) & 0x1f) << 3);
                                //image[j].A = (byte)(((p & 0x8000) != 0) ? 0xff : 0);
                                image[j] = RGBA.fromColor(Tinke.Convertir.BGR555(mat.Image[j * 2], mat.Image[j * 2 + 1]));
                                UInt16 p = (ushort)(mat.Image[j * 2] + (mat.Image[j * 2 + 1] << 8));
                                image[j].A = (byte)(((p & 0x8000) != 0) ? 0xff : 0);
                            }
                            break;
                    }

                    /////////////////////////////////////////////////////
                    // The trick to handle texture repetition in OpenGL
                    // Flip is not supported in Win32 version of OpenGL
                    // We have to manually resize the texture, and apply mirror/flip effect

                    /*if (mat.repeat == 0x07)
                    {
                        // repeat in s & t direction, flip in s direction
                        // double the width, add a mirror image along the right edge of the texture (s direction)
                        var newimage = new RGBA[pixelnum * 2];


                        int newwidth = mat.width * 2;
                        int newwidth_1 = newwidth - 1;
                        for (int y = 0; y < mat.height; y++)
                        {
                            int tbase = y * mat.width; // base in original texture
                            int newbase = y * newwidth; // base in new texture
                            for (int x = 0; x < mat.width; x++)
                            {
                                var pixel = image[tbase + x];
                                newimage[newbase + x] = pixel;
                                newimage[newbase + newwidth_1 - x] = pixel;
                            }
                        }
                        mat.width = newwidth;
                        image = newimage;
                    }
                        /*
                    else if (mat.repeat == 0x0b)
                    {
                        // repeat in s & t direction, flip in t direction
                        // double the height, add a mirror image along the bottom edge of the texture (t direction)
                        var newimage = new RGBA[pixelnum * 2];


                        int newheight = mat.height * 2;
                        int newheight_1 = mat.height - 1;
                        for (int y = 0; y < mat.height; y++)
                        {
                            int tbase = y * mat.width;
                            int newbase = (newheight_1 - y) * mat.width;
                            for (int x = 0; x < mat.width; x++)
                            {
                                newimage[newbase + x] = image[tbase + x];
                            }
                        }
                        mat.height = newheight;

                        image = newimage;
                    }
                         
                    else if (mat.repeat == 0x0f)
                    {
                        // repeat in s & t direction, flip in s & t direction
                        // double both width and height, add mirror images along both right and bottom edges
                        var newimage = new RGBA[pixelnum * 4];


                        int newwidth = mat.width * 2;
                        int newwidth_1 = newwidth - 1;
                        int newheight = mat.height * 2;
                        int newheight_1 = newheight - 1;
                        for (int y = 0; y < mat.height; y++)
                        {
                            int tbase = y * mat.width; // base in original texture
                            int topbase = y * newwidth; // top base in new texture
                            int bottombase = (newheight_1 - y) * newwidth; // bottom base in new texture
                            for (int x = 0; x < mat.width; x++)
                            {
                                var pixel = image[tbase + x];
                                newimage[topbase + x] = pixel;
                                newimage[topbase + newwidth_1 - x] = pixel;
                                newimage[bottombase + x] = pixel;
                                newimage[bottombase + newwidth_1 - x] = pixel;
                            }
                        }
                        mat.width = newwidth;
                        mat.height = newheight;

                        image = newimage;
                    }*/


                    Console.WriteLine("convert matid = {0}", i);
                    //Console.WriteLine("\ttex '{0}': {1} [{2},{3}] texsize = {4}", mat.texname, TEXTURE_FORMATS[mat.format], mat.width,
                    //                  mat.height, mat.texsize);
                    //Console.WriteLine("\tpal '{0}': pixelnum = {1}, repeat = {2}", mat.palname, pixelnum, mat.repeat);

                    var imageBytesList = new List<byte>();
                    for (int k = 0; k < image.Length; ++k)
                    {
                        imageBytesList.Add(image[k].R);
                        imageBytesList.Add(image[k].G);
                        imageBytesList.Add(image[k].B);
                        imageBytesList.Add((byte)(image[k].A/* - (255 - (((mat.Alpha + 1) * 8) - 1))*/));
                    }

                    var imageBytes = imageBytesList.ToArray();

                    Gl.glEnable(Gl.GL_ALPHA_TEST);

                    Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
                    Gl.glEnable(Gl.GL_BLEND);
                    Gl.glAlphaFunc(Gl.GL_GREATER, 0f);
                    if (mat.TexFormat == 1 || mat.TexFormat == 6) { Gl.glColor4f(1.0f, 1.0f, 1.0f, 0.0f); }
                    //Gl.glColor4f(1.0f, 1.0f, 1.0f, /*(float)mat.Alpha / 31.0f*/0.0f);
                    Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);

                   /* if (((mat.PolyAttrib >> 0xf) & 0x1) == 1 && (((mat.PolyAttrib >> 0) & 0x1) == 0 && ((mat.PolyAttrib >> 1) & 0x1) == 0 && ((mat.PolyAttrib >> 2) & 0x1) == 0 && ((mat.PolyAttrib >> 2) & 0x1) == 0) == false)
                    {
                        Gl.glEnable(Gl.GL_LIGHTING);
                        Gl.glLightf(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, mat.DiffuseColor.ToArgb());
                        Gl.glLightf(Gl.GL_LIGHT0, Gl.GL_AMBIENT, mat.AmbientColor.ToArgb());
                        Gl.glLightf(Gl.GL_LIGHT0, Gl.GL_SPECULAR, mat.SpecularColor.ToArgb());
                        Gl.glLightf(Gl.GL_LIGHT0, Gl.GL_EMISSION, mat.EmissionColor.ToArgb());
                        if (((mat.PolyAttrib >> 0) & 0x1) == 1) Gl.glEnable(Gl.GL_LIGHT0);
                        else Gl.glDisable(Gl.GL_LIGHT0);
                        if (((mat.PolyAttrib >> 1) & 0x1) == 1) Gl.glEnable(Gl.GL_LIGHT1);
                        else Gl.glDisable(Gl.GL_LIGHT1);
                        if (((mat.PolyAttrib >> 2) & 0x1) == 1) Gl.glEnable(Gl.GL_LIGHT2);
                        else Gl.glDisable(Gl.GL_LIGHT2);
                        if (((mat.PolyAttrib >> 3) & 0x1) == 1) Gl.glEnable(Gl.GL_LIGHT3);
                        else Gl.glDisable(Gl.GL_LIGHT3);

                        Gl.glMateriali(Gl.GL_FRONT_AND_BACK, Gl.GL_DIFFUSE, mat.DiffuseColor.ToArgb());
                        Gl.glMateriali(Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT, mat.AmbientColor.ToArgb());
                        Gl.glMateriali(Gl.GL_FRONT_AND_BACK, Gl.GL_SPECULAR, mat.SpecularColor.ToArgb());
                        Gl.glMateriali(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, mat.EmissionColor.ToArgb());

                    }
                    else
                    {
                        Gl.glDisable(Gl.GL_LIGHTING);
                    }*/


                    Gl.glBindTexture(Gl.GL_TEXTURE_2D, i + 1);
                    /*int mode = -1;
                    switch ((mat.PolyAttrib >> 4) & 0x3)
                    {
                        case 0:
                            mode = Gl.GL_MODULATE;
                            break;
                        case 1:
                            mode = Gl.GL_DECAL;
                            break;
                        case 2:
                            mode = Gl.GL_MODULATE;
                            break;
                        case 3:
                            mode = Gl.GL_MODULATE;
                            break;
                    }*/
                    Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_MODULATE);
                    /*int cullmode = -1;
                    switch (mat.PolyAttrib & 0xC0)
                    {
                        case 0x00: cullmode = Gl.GL_FRONT_AND_BACK; break;
                        case 0x40: cullmode = Gl.GL_FRONT; break;
                        case 0x80: cullmode = Gl.GL_BACK; break;
                    }
                    Gl.glCullFace(cullmode);*/
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, mat.Width, mat.Height, 0, Gl.GL_RGBA,
                                    Gl.GL_UNSIGNED_BYTE,
                                    imageBytes);
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST);


                    /*if (mat.flipS == 1 && mat.repeatS == 1)
                    {
                        Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_MIRRORED_REPEAT);
                        //Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
                    }
                    else if (mat.repeatS == 1)
                    {
                        //Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
                        Gl.glTexParameteri(3553, 10242, 10497);
                    }
                    else
                    {
                        Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP);
                    }
                    if (mat.flipT == 1 && mat.repeatT == 1)
                    {
                        Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_MIRRORED_REPEAT);
                        //Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
                    }
                    else if (mat.repeatT == 1)
                    {
                        //Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
                        Gl.glTexParameteri(3553, 10243, 10497);
                    }
                    else
                    {
                        Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP);
                    }*/
                    /* Gl.glEnable(Gl.GL_COLOR_MATERIAL);
                     Gl.glColorMaterial(Gl.GL_FRONT_AND_BACK, Gl.GL_DIFFUSE);
                     Gl.glColor3f((float)mat.DiffuseColor.R / 255f, (float)mat.DiffuseColor.G / 255f, (float)mat.DiffuseColor.B / 255f);
                     Gl.glColorMaterial(Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT);
                     Gl.glColor3f((float)mat.AmbientColor.R / 255f, (float)mat.AmbientColor.G / 255f, (float)mat.AmbientColor.B / 255f);
                     Gl.glColorMaterial(Gl.GL_FRONT_AND_BACK, Gl.GL_SPECULAR);
                     Gl.glColor3f((float)mat.SpecularColor.R / 255f, (float)mat.SpecularColor.G / 255f, (float)mat.SpecularColor.B / 255f);
                     Gl.glColorMaterial(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION);
                     Gl.glColor3f((float)mat.EmissionColor.R / 255f, (float)mat.EmissionColor.G / 255f, (float)mat.EmissionColor.B / 255f);*/

                    /*switch (mat.repeatS)
                    {
                        case 1: // '\0'
                            Gl.glTexParameteri(3553, 10242, 10497);
                            break;

                        case 0: // '\001'
                            Gl.glTexParameteri(3553, 10242, 33071);
                            break;
                    }
                    switch (mat.repeatT)
                    {
                        case 1: // '\0'
                            Gl.glTexParameteri(3553, 10243, 10497);
                            break;

                        case 0: // '\001'
                            Gl.glTexParameteri(3553, 10243, 33071);
                            break;
                    }*/
                    /*
                    switch (mat.repeat)
                    {
                        case 0x00:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP);
                            break;
                        case 0x01:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP);
                            break;
                        case 0x02:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
                            break;
                        case 0x03:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
                            break;
                        case 0x05:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_MIRRORED_REPEAT);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP);
                            break;
                        case 0x07:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_MIRRORED_REPEAT);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
                            break;
                        case 0x0A:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_MIRRORED_REPEAT);
                            break;
                        case 0x0B:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_MIRRORED_REPEAT);
                            break;
                        case 0x0F:
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_MIRRORED_REPEAT);
                            Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_MIRRORED_REPEAT);
                            break;
                    }*/
                }
                catch
                {

                }
            }

        }
        /// <summary>
        /// Convert texel.
        /// </summary>
        private bool convert_4x4texel(uint[] tex, int width, int height, UInt16[] data, Color[] pal, RGBA[] rgbaOut)
        {
            int w = width / 4;
            int h = height / 4;

            // traverse 'w x h blocks' of 4x4-texel
            for (int y = 0; y < h; y++)
                for (int x = 0; x < w; x++)
                {
                    int index = y * w + x;
                    UInt32 t = tex[index];
                    UInt16 d = data[index];
                    UInt16 addr = (ushort)(d & 0x3fff);
                    UInt16 mode = (ushort)((d >> 14) & 3);

                    // traverse every texel in the 4x4 texels
                    for (int r = 0; r < 4; r++)
                        for (int c = 0; c < 4; c++)
                        {
                            int texel = (int)((t >> ((r * 4 + c) * 2)) & 3);
                            Color pixel = rgbaOut[(y * 4 + r) * width + (x * 4 + c)].ToColor();

                            switch (mode)
                            {
                                case 0:
                                    pixel = pal[(addr << 1) + texel];
                                    if (texel == 3) pixel = Color.Transparent; // make it transparent, alpha = 0
                                    break;
                                case 2:
                                    pixel = pal[(addr << 1) + texel];
                                    break;
                                case 1:
                                    switch (texel)
                                    {
                                        case 0:
                                        case 1:
                                            pixel = pal[(addr << 1) + texel];
                                            break;
                                        case 2:
                                            pixel = Color.FromArgb(0xff, (int)((pal[(addr << 1)].R + pal[(addr << 1) + 1].R) / 2L), (int)((pal[(addr << 1)].G + pal[(addr << 1) + 1].G) / 2L), (int)((pal[(addr << 1)].B + pal[(addr << 1) + 1].B) / 2L));
                                            break;
                                        case 3:
                                            pixel = Color.Transparent; // make it transparent, alpha = 0
                                            break;
                                    }
                                    break;
                                case 3:
                                    switch (texel)
                                    {
                                        case 0:
                                        case 1:
                                            pixel = pal[(addr << 1) + texel];
                                            break;
                                        case 2:
                                            pixel = Color.FromArgb(0xff, (int)((pal[(addr << 1)].R * 5L + pal[(addr << 1) + 1].R * 3L) / 8), (int)((pal[(addr << 1)].G * 5L + pal[(addr << 1) + 1].G * 3L) / 8), (int)((pal[(addr << 1)].B * 5L + pal[(addr << 1) + 1].B * 3L) / 8));
                                            break;
                                        case 3:
                                            pixel = Color.FromArgb(0xff, (int)((pal[(addr << 1)].R * 3L + pal[(addr << 1) + 1].R * 5L) / 8), (int)((pal[(addr << 1)].G * 3L + pal[(addr << 1) + 1].G * 5L) / 8), (int)((pal[(addr << 1)].B * 3L + pal[(addr << 1) + 1].B * 5L) / 8));
                                            break;
                                    }
                                    break;
                            }
                            rgbaOut[(y * 4 + r) * width + (x * 4 + c)] = RGBA.fromColor(pixel);
                        }
                }
            return true;
        }

        /// <summary>
        /// Convert texel (wrapper for type safety issues).
        /// </summary>
        private void convert_4x4texel_b(byte[] tex, int width, int height, byte[] data, Color[] pal, RGBA[] rgbaOut)
        {
            var list1 = new List<uint>();
            for (int i = 0; i < (tex.Length + 1) / 4; ++i)
                list1.Add(Utils.Read4BytesAsUInt32(tex, i * 4));

            var list2 = new List<UInt16>();
            for (int i = 0; i < (data.Length + 1) / 2; ++i)
                list2.Add(Utils.Read2BytesAsUInt16(data, i * 2));
            var b = convert_4x4texel(list1.ToArray(), width, height, list2.ToArray(), pal, rgbaOut);
        }
        private void RenderPrimitives(byte[] polydata)
        {
            //bool start = false;
            int nr = 1;
            if (polydata == null)
                return;
            int commandptr = 0;
            int commandlimit = polydata.Length;
            int[] command = new int[4];
            int cur_vertex, mode, i;
            float[] vtx_state = {0.0f, 0.0f, 0.0f};
            float[] vtx_trans = {0.0f, 0.0f, 0.0f};
            //cur_vertex = gCurrentVertex; // for vertex_mode
            /*if (Model.Objects.Count > 0)
            {
                CurrentMatrix = MatrixStack[stackID].Clone();
            }
            else
            {
                CurrentMatrix.LoadIdentity();
            }*/
            while (commandptr < commandlimit)
            {
                for (i = 0; i < 4; ++i)
                {
                    if (commandptr >= commandlimit)
                        command[i] = 0xFF;
                    else
                    {
                        command[i] = polydata[commandptr];
                        commandptr++;
                    }
                }


                for (i = 0; i < 4 && commandptr < commandlimit; i++)
                {
                   /* if (start == false && command[i] != 0x40)
                    {
                        switch (command[i])
                        {
                            case 0: break;
                            case 0x10: commandptr += 4; break;
                            case 0x11: break;
                            case 0x12: commandptr += 4; break;
                            case 0x13: commandptr += 4; break;
                            case 0x14: commandptr += 4; break;
                            case 0x15: break;
                            case 0x16:
                                {
                                    for (int j = 0; j < 16; j++)
                                    {
                                        commandptr += 4;
                                    }
                                    break;
                                }
                            case 0x17:
                                {
                                    for (int j = 0; j < 4; j++)
                                    {
                                        for (int k = 0; k < 3; j++)
                                        {
                                            commandptr += 4;
                                        }
                                    }
                                    break;
                                }
                            case 0x18:
                                {
                                    for (int j = 0; j < 16; j++)
                                    {
                                        commandptr += 4;
                                    }
                                    break;
                                }
                            case 0x19:
                                {
                                    for (int j = 0; j < 4; j++)
                                    {
                                        for (int k = 0; k < 3; j++)
                                        {
                                            commandptr += 4;
                                        }
                                    }
                                    break;
                                }
                            case 0x1A:
                                {
                                    for (int j = 0; j < 3; j++)
                                    {
                                        for (int k = 0; k < 3; j++)
                                        {
                                            commandptr += 4;
                                        }
                                    }
                                    break;
                                }
                            case 0x1b: commandptr += 12; break;
                            case 0x1c: commandptr += 4; break;
                            case 0x20: commandptr += 4; break;
                            case 0x21: commandptr += 4; break;
                            case 0x22: commandptr += 4; break;
                            case 0x23: commandptr += 8; break;
                            case 0x24: commandptr += 4; break;
                            case 0x25: commandptr += 4; break;
                            case 0x26: commandptr += 4; break;
                            case 0x27: commandptr += 4; break;
                            case 0x28: commandptr += 4; break;
                            case 0x29: commandptr += 4; break;
                            case 0x2A: commandptr += 4; break;
                            case 0x2B: commandptr += 4; break;

                            // lighting commands
                            case 0x30: commandptr += 4; break;
                            case 0x31: commandptr += 4; break;
                            case 0x32: commandptr += 4; break;
                            case 0x33: commandptr += 4; break;
                            case 0x34: commandptr += 128; break;
                            case 0x40: commandptr += 4; break;
                            case 0x41: break;
                            case 0x50: commandptr += 4; break;
                            case 0x60: commandptr += 4; break;
                            case 0x70: commandptr += 12; break;
                            case 0x71: commandptr += 8; break;
                            case 0x72: commandptr += 4; break;
                            default:
                                continue;
                        }
                        continue;
                    }*/
                    switch (command[i])
                    {
                        case 0: // No Operation (for padding packed GXFIFO commands)
                            break;
                        case 0x10:
                            {
                                int param = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                switch (param)
                                {
                                    case 0:
                                       Gl.glMatrixMode(Gl.GL_PROJECTION_MATRIX);
                                        break;
                                    case 1:
                                       Gl.glMatrixMode(Gl.GL_MODELVIEW_MATRIX);
                                        break;
                                    case 2:
                                        break;
                                    case 3:
                                       Gl.glMatrixMode(Gl.GL_TEXTURE_MATRIX);
                                        break;
                                }
                                break;
                            }
                        case 0x11: break;
                        case 0x12: commandptr += 4; break;
                        case 0x13: commandptr += 4; break;
                        case 0x14:
                            /*
                                  MTX_RESTORE - Restore Current Matrix from Stack (W)
                                  Sets C=[N]. The stack pointer S is not used, and is left unchanged.
                                  Parameter Bit0-4:  Stack Address (0..30) (31 causes overflow in GXSTAT.15)
                                  Parameter Bit5-31: Not used
                                */

                            //stackID = Utils.Read4BytesAsInt32(polydata, commandptr) & 0x1F;// & 0x0000001F;
                            commandptr += 4;
                            //MatrixStack[stackID].CopyValuesTo(CurrentMatrix);
                            break;
                        case 0x15:
                            {
                                //CurrentMatrix.LoadIdentity();
                                //Gl.glLoadIdentity();
                                break;
                            }
                        case 0x16:
                            {
                                for (int j = 0; j < 16; j++)
                                {
                                    //CurrentMatrix[j] = (float)Utils.Read4BytesAsInt32(polydata, commandptr) / 4096f;
                                    commandptr += 4;
                                }
                                break;
                            }
                        case 0x17:
                            {
                                for (int j = 0; j < 4; j++)
                                {
                                    for (int k = 0; k < 3; j++)
                                    {
                                        //CurrentMatrix[k,j] = (float)Utils.Read4BytesAsInt32(polydata, commandptr) / 4096f;
                                        commandptr += 4;
                                    }
                                }
                                break;
                            }
                        case 0x18:
                            {
                                MTX44 f = new MTX44();
                                f.LoadIdentity();
                                for (int j = 0; j < 16; j++)
                                {
                                    f[j] = (float)Utils.Read4BytesAsInt32(polydata, commandptr) / 4096f;
                                    commandptr += 4;
                                }
                                //CurrentMatrix.MultMatrix(f).CopyValuesTo(CurrentMatrix);
                                //Gl.glLoadMatrixf(f.Floats);
                                break;
                            }
                        case 0x19:
                            {
                                MTX44 f = new MTX44();
                                f.LoadIdentity();
                                for (int j = 0; j < 4; j++)
                                {
                                    for (int k = 0; k < 3; j++)
                                    {
                                        f[k, j] = (float)Utils.Read4BytesAsInt32(polydata, commandptr) / 4096f;
                                        commandptr += 4;
                                    }
                                }
                                //CurrentMatrix.MultMatrix(f).CopyValuesTo(CurrentMatrix);
                                //Gl.glLoadMatrixf(f.Floats);
                                break;
                            }
                        case 0x1A:
                            {
                                MTX44 f = new MTX44();
                                f.LoadIdentity();
                                for (int j = 0; j < 3; j++)
                                {
                                    for (int k = 0; k < 3; j++)
                                    {
                                        f[k, j] = (float)Utils.Read4BytesAsInt32(polydata, commandptr) / 4096f;
                                        commandptr += 4;
                                    }
                                }
                                //CurrentMatrix.MultMatrix(f).CopyValuesTo(CurrentMatrix);
                                //Gl.glLoadMatrixf(f.Floats);
                                break;
                            }
                        case 0x1b:
                            /*
                                  MTX_SCALE - Multiply Current Matrix by Scale Matrix (W)
                                  Sets C=M*C. Parameters: 3, m[0..2] (MTX_SCALE doesn't change Vector Matrix)
                                */
                            {
                                int x, y, z;
                                x = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                y = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                z = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                //CurrentMatrix.SetValues(scale(CurrentMatrix.Floats, x, y, z));
                                //CurrentMatrix.Scale(x/SCALE_IV, y/SCALE_IV, z/SCALE_IV);
                                //CurrentMatrix.Scale((float)sign(x, 32) / SCALE_IV, (float)sign(y, 32) / SCALE_IV, (float)sign(z, 32) / SCALE_IV);
                                break;
                            }
                        case 0x1c:
                            /*
                                  MTX_TRANS - Mult. Curr. Matrix by Translation Matrix (W)
                                  Sets C=M*C. Parameters: 3, m[0..2] (x,y,z position)
                                */
                            {
                                float x, y, z;
                                x = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                y = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                z = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                //CurrentMatrix.translate(x / SCALE_IV, y / SCALE_IV, z / SCALE_IV);
                                //Gl.glTranslatef(x / SCALE_IV, y / SCALE_IV, z / SCALE_IV);
                                break;
                            }
                        case 0x20: // Directly Set Vertex Color (W)
                            {
                                Int64 rgb, r, g, b;

                                rgb = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                if (true)
                                {
                                    r = (rgb >> 0) & 0x1F;
                                    g = (rgb >> 5) & 0x1F;
                                    b = (rgb >> 10) & 0x1F;
                                    Gl.glColor3f(((float) r)/31.0f, ((float) g)/31.0f, ((float) b)/31.0f);
                                }
                            }
                            break;

                        case 0x21:
                            /*
                                  Set Normal Vector (W)
                                  0-9   X-Component of Normal Vector (1bit sign + 9bit fractional part)
                                  10-19 Y-Component of Normal Vector (1bit sign + 9bit fractional part)
                                  20-29 Z-Component of Normal Vector (1bit sign + 9bit fractional part)
                                  30-31 Not used
                                */
                            {
                                Int64 xyz, x, y, z;

                                xyz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                x = (xyz >> 0) & 0x3FF;
                                if ((x & 0x200) != 0)
                                    x |= -1024;
                                y = (xyz >> 10) & 0x3FF;
                                if ((y & 0x200) != 0) y |= -1024;
                                z = (xyz >> 20) & 0x3FF;
                                if ((z & 0x200) != 0) z |= -1024;
                                Gl.glNormal3f(((float) x)/512.0f, ((float) y)/512.0f, ((float) z)/512.0f);
                                /*if (writevertex)
                                {
                                    //normals.Add(new float[] { ((float)x) / 512.0f, ((float)y) / 512.0f, ((float)z) / 512.0f });
                                    mod.Normals.Add(new System.Windows.Media.Media3D.Vector3D(((float)x) / 512.0f, ((float)y) / 512.0f, ((float)z) / 512.0f));
                                }*/
                                break;
                            }
                        case 0x22:
                            /*
                                  Set Texture Coordinates (W)
                                  Parameter 1, Bit 0-15   S-Coordinate (X-Coordinate in Texture Source)
                                  Parameter 1, Bit 16-31  T-Coordinate (Y-Coordinate in Texture Source)
                                  Both values are 1bit sign + 11bit integer + 4bit fractional part.
                                  A value of 1.0 (=1 SHL 4) equals to one Texel.
                                */
                            {
                                Int64 st, s, t;

                                st = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                s = (st >> 0) & 0xffff;
                                if ((s & 0x8000) != 0) s |= unchecked((int)0xFFFF0000);//-65536;
                                t = (st >> 16) & 0xffff;
                                if ((t & 0x8000) != 0) t |= unchecked((int)0xFFFF0000);//-65536;
                                Gl.glTexCoord2f(((float) s)/16.0f, ((float) t)/16.0f);

                                /*if (writevertex)
                                {
                                    mod.TextureCoordinates.Add(new System.Windows.Point((float)s/1024f,(float)t/1024f));
                                }*/
                                break;
                            }
                        case 0x23:
                            /*
                                  VTX_16 - Set Vertex XYZ Coordinates (W)
                                  Parameter 1, Bit 0-15   X-Coordinate (signed, with 12bit fractional part)
                                  Parameter 1, Bit 16-31  Y-Coordinate (signed, with 12bit fractional part)
                                  Parameter 2, Bit 0-15   Z-Coordinate (signed, with 12bit fractional part)
                                  Parameter 2, Bit 16-31  Not used
                                */
                            {
                                int parameter, x, y, z;

                                parameter = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                x = sign( (parameter >> 0) & 0xFFFF,16);
                                //if ((x & 0x8000) != 0) x |= unchecked((int)0xFFFF0000);//-65536;
                                y = sign((parameter >> 16) & 0xFFFF,16);
                                //if ((y & 0x8000) != 0) y |= unchecked((int)0xFFFF0000);//-65536;

                                parameter = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                z = sign(parameter & 0xFFFF,16);
                               // if ((z & 0x8000) != 0) z |= unchecked((int)0xFFFF0000);//-65536;
                               
                                vtx_state[0] = ((float) x)/SCALE_IV;
                                vtx_state[1] = ((float) y)/SCALE_IV;
                                vtx_state[2] = ((float) z)/SCALE_IV;
                              /*if (stackID != -1)
                                {
                                    vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                    Gl.glVertex3fv(vtx_trans);
                                    if (writevertex)
                                    {
                                        //vertex.Add(vtx_trans);
                                        mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_trans[0], vtx_trans[1], vtx_trans[2]));
                                    }
                                }
                                else*/
                                {
                                    Gl.glVertex3fv(vtx_state);
                                   // if (writevertex)
                                    //{
                                        //vertex.Add(vtx_state);
                                    //    mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_state[0], vtx_state[1], vtx_state[2]));
                                    //}
                                }
                              // if (writevertex)
                              // {
                                   //vertex_normal.Add((normals.Count == 0 ? -1 : normals.Count));
                              // }
                                break;
                            }
                        case 0x24:
                            /*
                                  VTX_10 - Set Vertex XYZ Coordinates (W)
                                  Parameter 1, Bit 0-9    X-Coordinate (signed, with 6bit fractional part)
                                  Parameter 1, Bit 10-19  Y-Coordinate (signed, with 6bit fractional part)
                                  Parameter 1, Bit 20-29  Z-Coordinate (signed, with 6bit fractional part)
                                  Parameter 1, Bit 30-31  Not used
                                */
                            {
                                int xyz, x, y, z;

                                xyz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                x = sign((xyz >> 0) & 0x3FF, 10);
                               // if ((x & 0x200) != 0) x |=  unchecked((int)0xFFFFFC00);//-1024;
                                y = sign((xyz >> 10) & 0x3FF, 10);
                                //if ((y & 0x200) != 0) y |=  unchecked((int)0xFFFFFC00);//-1024;
                                z = sign((xyz >> 20) & 0x3FF, 10);
                               // if ((z & 0x200) != 0) z |= unchecked((int)0xFFFFFC00);//-1024;

                                vtx_state[0] = (float)x/64.0f;
                                vtx_state[1] = (float)y/64.0f;
                                vtx_state[2] = (float)z/64.0f;
                              /* if (stackID != -1)
                                {
                                    vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                    Gl.glVertex3fv(vtx_trans);
                                    if (writevertex)
                                    {
                                        //vertex.Add(vtx_trans);
                                        mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_trans[0],vtx_trans[1],vtx_trans[2]));
                                    }
                                }
                               else*/
                                {
                                   Gl.glVertex3fv(vtx_state);
                                   //if (writevertex)
                                   {
                                       //vertex.Add(vtx_state);
                                     //  mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_state[0], vtx_state[1], vtx_state[2]));
                                   }
                                }
                              // if (writevertex)
                               {
                                   //vertex_normal.Add((normals.Count == 0 ? -1 : normals.Count));
                               }
                                break;
                            }
                        case 0x25:
                            /*
                                  VTX_XY - Set Vertex XY Coordinates (W)
                                  Parameter 1, Bit 0-15   X-Coordinate (signed, with 12bit fractional part)
                                  Parameter 1, Bit 16-31  Y-Coordinate (signed, with 12bit fractional part)
                                */
                            {
                               int xy, x, y;

                                xy = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                x = sign((xy >> 0) & 0xFFFF, 16);
                                //if ((x & 0x8000) != 0) x |=  unchecked((int)0xFFFF0000);//-65536;
                                y = sign((xy >> 16) & 0xFFFF, 16);
                                //if ((y & 0x8000) != 0) y |= unchecked((int)0xFFFF0000);//-65536;

                                vtx_state[0] = ((float) x)/SCALE_IV;
                                vtx_state[1] = ((float) y)/SCALE_IV;
                                /* if (stackID != -1)
                                 {
                                   vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                    Gl.glVertex3fv(vtx_trans);
                                    if (writevertex)
                                    {
                                        //vertex.Add(vtx_trans);
                                        mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_trans[0], vtx_trans[1], vtx_trans[2]));
                                    }
                                }
                                else*/
                                {
                                    Gl.glVertex3fv(vtx_state);
                                    //if (writevertex)
                                    {
                                        //vertex.Add(vtx_state);
                                     //   mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_state[0], vtx_state[1], vtx_state[2]));
                                    }
                                }
                                // if (writevertex)
                                 {
                                     //vertex_normal.Add((normals.Count == 0 ? -1 : normals.Count));
                                 }
                                break;
                            }
                        case 0x26:
                            /*
                                  VTX_XZ - Set Vertex XZ Coordinates (W)
                                  Parameter 1, Bit 0-15   X-Coordinate (signed, with 12bit fractional part)
                                  Parameter 1, Bit 16-31  Z-Coordinate (signed, with 12bit fractional part)
                                */
                            {
                                int xz, x, z;

                                xz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                x = sign((xz >> 0) & 0xFFFF, 16);
                               // if ((x & 0x8000) != 0) x |= unchecked((int)0xFFFF0000);//-65536;
                                z = sign((xz >> 16) & 0xFFFF, 16);
                               // if ((z & 0x8000) != 0) z |= unchecked((int)0xFFFF0000);//-65536;

                                vtx_state[0] = ((float) x)/SCALE_IV;
                                vtx_state[2] = ((float) z)/SCALE_IV;
                               /* if (stackID != -1)
                                {
                                    vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                   Gl.glVertex3fv(vtx_trans);
                                   if (writevertex)
                                   {
                                       //vertex.Add(vtx_trans);
                                       mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_trans[0], vtx_trans[1], vtx_trans[2]));
                                   }
                                }
                                else*/
                                {
                                    Gl.glVertex3fv(vtx_state);
                                    //if (writevertex)
                                    {
                                        //vertex.Add(vtx_state);
                                        //mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_state[0], vtx_state[1], vtx_state[2]));
                                    }
                                }
                                //if (writevertex)
                                {
                                   // vertex_normal.Add((normals.Count == 0 ? -1 : normals.Count));
                                }
                                break;
                            }
                        case 0x27:
                            /*
                                  VTX_YZ - Set Vertex YZ Coordinates (W)
                                  Parameter 1, Bit 0-15   Y-Coordinate (signed, with 12bit fractional part)
                                  Parameter 1, Bit 16-31  Z-Coordinate (signed, with 12bit fractional part)
                                */
                            {
                                int yz, y, z;
                                yz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                y = sign((yz >> 0) & 0xFFFF, 16);
                                //if ((y & 0x8000) != 0) y |= unchecked((int)0xFFFF0000);//-65536;
                                z = sign((yz >> 16) & 0xFFFF, 16);
                                //if ((z & 0x8000) != 0) z |= unchecked((int)0xFFFF0000);//-65536;

                                vtx_state[1] = ((float) y)/SCALE_IV;
                                vtx_state[2] = ((float) z)/SCALE_IV;
                                /*if (stackID != -1)
                                {
                                    vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                   Gl.glVertex3fv(vtx_trans);
                                   if (writevertex)
                                   {
                                       //vertex.Add(vtx_trans);
                                       mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_trans[0], vtx_trans[1], vtx_trans[2]));
                                   }
                                }
                                else*/
                                {
                                    Gl.glVertex3fv(vtx_state);
                                    //if (writevertex)
                                    {
                                        //vertex.Add(vtx_state);
                                     //   mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_state[0], vtx_state[1], vtx_state[2]));
                                    }
                                }
                               // if (writevertex)
                                {
                                    //vertex_normal.Add((normals.Count == 0 ? -1 : normals.Count));
                                }
                                break;
                            }
                        case 0x28:
                            /*
                                  VTX_DIFF - Set Relative Vertex Coordinates (W)
                                  Parameter 1, Bit 0-9    X-Difference (signed, with 9bit fractional part)
                                  Parameter 1, Bit 10-19  Y-Difference (signed, with 9bit fractional part)
                                  Parameter 1, Bit 20-29  Z-Difference (signed, with 9bit fractional part)
                                  Parameter 1, Bit 30-31  Not used
                                */
                            {
                                int xyz, x, y, z;
                                xyz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;

                                x = sign((xyz >> 0) & 0x3FF, 10);
                                //if ((x & 0x200) != 0) x |= unchecked((int)0xFFFFFC00);//-1024;
                                y = sign((xyz >> 10) & 0x3FF, 10);
                                //if ((y & 0x200) != 0) y |= unchecked((int)0xFFFFFC00);
                                z = sign((xyz >> 20) & 0x3FF, 10);
                                //if ((z & 0x200) != 0) z |= unchecked((int)0xFFFFFC00);


                                vtx_state[0] += ((float)x) / SCALE_IV;
                                vtx_state[1] += ((float)y) / SCALE_IV;
                                vtx_state[2] += ((float)z) / SCALE_IV;
                               /* if (stackID != -1)
                                {
                                    vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                   Gl.glVertex3fv(vtx_trans);
                                   if (writevertex)
                                   {
                                       //vertex.Add(vtx_trans);
                                       mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_trans[0], vtx_trans[1], vtx_trans[2]));
                                   }
                                }
                                else*/
                                {
                                    Gl.glVertex3fv(vtx_state);
                                    //if (writevertex)
                                    {
                                        //vertex.Add(vtx_state);
                                        //mod.Positions.Add(new System.Windows.Media.Media3D.Point3D(vtx_state[0], vtx_state[1], vtx_state[2]));
                                    }
                                }
                                //if (writevertex)
                                {
                                   // vertex_normal.Add((normals.Count == 0 ? -1 : normals.Count));
                                }
                                break;
                            }
                        case 0x29:
                            {
                                int param = Utils.Read4BytesAsInt32(polydata, commandptr);
                                commandptr += 4;
                                /*int light = (param >> 0) & 0x10;
                                int polygonMode = (param >> 4) & 0x4;
                                switch (polygonMode)
                                {
                                    case 0:
                                        mode = Gl.GL_MODULATE;
                                        break;
                                    case 1:
                                        mode = Gl.GL_DECAL;
                                        break;
                                    case 2:
                                        mode = Gl.GL_SHADOW_AMBIENT_SGIX;
                                        break;
                                    case 3:
                                        mode = Gl.GL_QUAD_STRIP;
                                        break;
                                    default:
                                        //return ;// FALSE;
                                        throw new Exception();
                                        break;
                                }*/
                                break;
                            }
                        case 0x2A: commandptr += 4; break;
                        case 0x2B: commandptr += 4; break;

                        // lighting commands
                        case 0x30: commandptr += 4; break;
                        case 0x31: commandptr += 4; break;
                        case 0x32: commandptr += 4; break;
                        case 0x33:
                            {   

                                commandptr += 4;
                                break;
                            }
                        case 0x34: commandptr += 128; break;
                        case 0x40: // Start of Vertex List (W)
                            {
                                mode = Utils.Read4BytesAsInt32(polydata, commandptr);
                                //mode = mode & 0x3;
                                commandptr += 4;

                                switch (mode)
                                {
                                    case 0:
                                        mode = Gl.GL_TRIANGLES;
                                        break;
                                    case 1:
                                        mode = Gl.GL_QUADS;
                                        break;
                                    case 2:
                                        mode = Gl.GL_TRIANGLE_STRIP;
                                        break;
                                    case 3:
                                        mode = Gl.GL_QUAD_STRIP;
                                        break;
                                    default:
                                        //return ;// FALSE;
                                        throw new Exception();
                                        break;
                                }

                                Gl.glBegin(mode);
                                //start = true;
                                break;
                            }
                        case 0x41: // End of Vertex List (W)
                            Gl.glEnd();
                            //mod.TriangleIndices.Add(nr);
                            //mod.Positions.Add(mod.Positions[mod.Positions.Count - 1]);
                            //mod.Positions.Add(mod.Positions[mod.Positions.Count - 1]);
                            //mod.Positions.Add(mod.Positions[mod.Positions.Count - 1]);
                            //mod.Normals.Add(mod.Normals[mod.Normals.Count - 1]);
                            //mod.Normals.Add(mod.Normals[mod.Normals.Count - 1]);
                            //mod.Normals.Add(mod.Normals[mod.Normals.Count - 1]);
                            nr++;
                            // for vertex mode, display at maximum certain number of vertex-list
                            // decrease cur_vertex so that when we reach 0, stop rendering any further
                            //cur_vertex--;
                            //if (cur_vertex < 0)
                            //    return; //TRUE;
                            //return;
                            //start = false;
                            return;
                            break;
                        case 0x50: commandptr += 4; break;
                        case 0x60: commandptr += 4; break;
                        case 0x70: commandptr += 12; break;
                        case 0x71: commandptr += 8; break;
                        case 0x72: commandptr += 4; break;
                        default:
                            break;
                            //return FALSE;
                    }
                }
            }
        
        }
    private int sign(int data, int size)
        {
            if ((data & 1 << size - 1) != 0)
                data |= -1 << size;
            return data;
        }
    }
}
